package common

import (
	"context"
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"log"
	"strconv"
	"strings"
	"time"
)

var (
	logger *zap.SugaredLogger
)

var levelMap = map[string]zapcore.Level{
	"debug":  zapcore.DebugLevel,
	"info":   zapcore.InfoLevel,
	"warn":   zapcore.WarnLevel,
	"error":  zapcore.ErrorLevel,
	"dpanic": zapcore.DPanicLevel,
	"panic":  zapcore.PanicLevel,
	"fatal":  zapcore.FatalLevel,
}

func LoggerLevel(lvl string) zapcore.Level {
	if level, ok := levelMap[lvl]; ok {
		return level
	}
	return zapcore.InfoLevel
}

func InitLog() (err error){

	logPath := GConfig.Log.Path + time.Now().Format("20060101") + ".log"

	writer := zapcore.AddSync(&lumberjack.Logger{
		Filename:  logPath,
		MaxSize:   1 << 30, //1G
		LocalTime: true,
		Compress:  true,
		MaxAge:    7, // days
	})
	level := zap.NewAtomicLevelAt(LoggerLevel(GConfig.Log.Level))
	core := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), writer, level)
	logger = zap.New(core).Sugar()

	return nil
}

func Debugf(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.DebugLevel, msg, output...)
	if logger != nil {
		logger.Debugw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Infof(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.InfoLevel, msg, output...)
	if logger != nil {
		logger.Infow(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Warnf(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.WarnLevel, msg, output...)
	if logger != nil {
		logger.Warnw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Errorf(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.ErrorLevel, msg, output...)
	if logger != nil {
		logger.Errorw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Fatalf(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.FatalLevel, msg, output...)
	if logger != nil {
		logger.Fatalw(msg, stack)
	} else {
		log.Fatal(msg)
	}
}

func Panicf(ctx context.Context, msg string, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.PanicLevel, msg, output...)
	if logger != nil {
		logger.Panicw(msg, stack)
	} else {
		log.Panic(msg)
	}
}

func Debug(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.DebugLevel, "", output...)
	if logger != nil {
		logger.Debugw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Info(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.InfoLevel, "", output...)
	if logger != nil {
		logger.Infow(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Warn(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.WarnLevel, "", output...)
	if logger != nil {
		logger.Warnw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Error(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.ErrorLevel, "", output...)
	if logger != nil {
		logger.Errorw(msg, stack)
	} else {
		log.Print(msg)
	}
}

func Fatal(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.FatalLevel, "", output...)
	if logger != nil {
		logger.Fatalw(msg, stack)
	} else {
		log.Fatal(msg)
	}
}

func Panic(ctx context.Context, output ...interface{}) {
	msg, stack := logMsg(ctx, zap.PanicLevel, "", output...)
	if logger != nil {
		logger.Panicw(msg, stack)
	} else {
		log.Panic(msg)
	}
}

func logMsg(ctx context.Context, lvl zapcore.Level, msg string, output ...interface{}) (string, zap.Field) {
	if msg == "" && len(output) > 0 {
		msg = fmt.Sprint(output...)
	} else if msg != "" && len(output) > 0 {
		msg = fmt.Sprintf(msg, output...)
	}
	logId := fmt.Sprintf("%d", time.Now().UnixNano())
	if ctx != nil {
		logId = strconv.FormatUint(uint64(ctx.Value(LOGID).(uint32)), 10)
	}
	msg = fmt.Sprintf("%s %s", logId, msg)
	msg = strings.Replace(msg, "\n\t", " ", -1)
	if zap.WarnLevel.Enabled(lvl) {
		return msg, zap.Stack("stack")
	}
	return msg + " ", zap.Skip()
}
